#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "errno.h"
#include "simconn.h"
#include "simconndata.h"

int fifo_fd[2];

int simconn_connect() {
	if (mkfifo("chipmuck-fifo-in", 0777) != 0)
		goto err_create;
	if (mkfifo("chipmuck-fifo-out", 0777) != 0)
		goto err_create;
	if ((fifo_fd[0] = open("chipmuck-fifo-in", O_RDONLY | O_NONBLOCK)) < 0)
		goto err_open1;
	if ((fifo_fd[1] = open("chipmuck-fifo-out", O_WRONLY)) < 0)
		goto err_open2;
	return 0;
err_open2:
	close(fifo_fd[0]);
err_open1:
	unlink("chipmuck-fifo-in");
	unlink("chipmuck-fifo-out");
	return -E_PIPE_OPEN_FAILED;
err_create:
	if (errno == EEXIST)
		fprintf(stderr, "chipmuck: the pipe is already existed, is another chipmuck running?\n");
	return -E_PIPE_CREATE_FAILED;
}

int simconn_disconnect() {
	close(fifo_fd[0]);
	close(fifo_fd[1]);
	unlink("chipmuck-fifo-in");
	unlink("chipmuck-fifo-out");
	return 0;
}

int simconn_wait_breakpoint(int wait_second, int *index) {
	int ret, packid;
	struct pack_bp_met pack;

	ret = read_pack(fifo_fd[0], &packid, &pack, sizeof(pack), wait_second);
	if (packid != PACKID_BP_MET)
		return -E_SIMULATOR_CRAZY;
	if (ret == 0)
		*index = pack.id;
	return ret;
}

int simconn_add_breakpoint(int type, void *data, int size, int *index) {
	int ret, packid;
	struct pack_bp_set pack;
	struct pack_bp_set_ret packret;

	pack.type = type;
	pack.datasize = size;
	if (size > 256)
		return -E_BPDATA_TOO_LARGE;
	if ((ret = write_pack(fifo_fd[1], PACKID_BP_SET, &pack, sizeof(pack))) < 0)
		return ret;
	if ((ret = write(fifo_fd[1], data, size)) < 0)
		return ret;
	while (read_pack(fifo_fd[0], &packid, &packret, sizeof(packret), 10) < 0);
	if (packid != PACKID_BP_SET_RET)
		return -E_SIMULATOR_CRAZY;
	return packret.ret;
}

int simconn_del_breakpoint(int index, int type) {
	int ret, packid;
	struct pack_bp_remove pack;
	struct pack_bp_remove_ret packret;

	pack.id = index;
	if ((ret = write_pack(fifo_fd[1], PACKID_BP_REMOVE, &pack, sizeof(pack))) < 0)
		return ret;
	while (read_pack(fifo_fd[0], &packid, &packret, sizeof(packret), 10) < 0);
	if (packid != PACKID_BP_REMOVE_RET)
		return -E_SIMULATOR_CRAZY;
	return packret.ret;
}

int simconn_dump_memory(unsigned int addr, void *data, int size) {
	int ret, packid;
	struct pack_mem_dump pack;
	struct pack_mem_dump_ret packret;

	pack.addr = addr;
	pack.size = size;
	if ((ret = write_pack(fifo_fd[1], PACKID_MEM_DUMP, &pack, sizeof(pack))) < 0)
		return ret;
	while (read_pack(fifo_fd[0], &packid, &packret, sizeof(packret), 10) < 0);
	if (packid != PACKID_MEM_DUMP_RET)
		return -E_SIMULATOR_CRAZY;
	if (packret.ret == 0)
		read_data(fifo_fd[0], data, size);
	return packret.ret;
}

int simconn_reg(unsigned int *regs, int *nreg) {
	int ret, packid;
	struct pack_reg_ret packret;

	if ((ret = write_pack(fifo_fd[1], PACKID_REG, regs, 0)) < 0)
		return ret;
	while (read_pack(fifo_fd[0], &packid, &packret, sizeof(packret), 10) < 0);
	if (packid != PACKID_REG_RET)
		return -E_SIMULATOR_CRAZY;
	memcpy(regs, packret.regs, sizeof(unsigned int) * packret.nreg);
	*nreg = packret.nreg;
	return packret.ret;
}

int simconn_continue() {
	return write_pack(fifo_fd[1], PACKID_CONTINUE, 0, 0);
}

